package com.googlecode.imanager.client;

import java.io.ObjectStreamException;

import com.googlecode.imanager.client.bridge.BridgeManager;
import com.googlecode.imanager.client.bridge.IDocument;
import com.googlecode.imanager.client.bridge.style.IParentThemeHandler;
import com.googlecode.imanager.client.bridge.style.ITheme;
import com.googlecode.imanager.client.io.Path;
import com.googlecode.imanager.client.layout.ScrollBox;
import com.googlecode.imanager.client.media.Image;
import com.googlecode.imanager.client.style.PageSettings;
import com.googlecode.imanager.client.style.Theme;
import com.googlecode.imanager.client.style.listener.StyleAttribute;
import com.googlecode.imanager.client.style.listener.StyleWrapper;
import com.googlecode.imanager.client.style.listener.ThemeListener;
import com.googlecode.imanager.client.util.Dimension;
import com.googlecode.imanager.client.util.HasTheme;
import com.googlecode.imanager.client.util.Utils;
import com.googlecode.imanager.client.value.ValueType;
import com.googlecode.imanager.client.value.format.Formatter;
import com.googlecode.imanager.client.value.format.ObjectFormatter;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public class Document extends ScrollBox implements IDocument, HasTheme {

    private static final long serialVersionUID = 1L;

    private Path basePath;
    private String title;
    private boolean modal;
    private PageSettings pageSettings;
    private Theme theme;    
    Application application;
    
    public Document() {
        super(400, 400);
    }

    public Document(Container parent) {
        this();
        setParent(parent);
    }
    
    public Document(Application application) {
        this();
        setApplication(application);
    }
    
    @Override
    protected void onBeforeSetParent(Container newParent) {
        super.onBeforeSetParent(newParent);
        if (newParent != null){
            setApplication(null);
        }
    }

    @Override
    protected void onAfterSetParent(Container oldParent) {
        super.onAfterSetParent(oldParent);
        if (!hasParent()){
            onParentResized(getParentSize());
        }
    }

    public final void setApplication(Application application) {
        if (this.application != application){
            Application oldApplication = this.application;

            if (oldApplication != null){
                this.application = null;
                oldApplication.getDocuments().remove(this);
                BridgeManager.getInstance().changeApplication(this, null);
                oldApplication.getBridge().remove(this);
                oldApplication.onDocumentRemoved(this);
            }

            if (application != null){
                setParent(null);
            }

            this.application = application;

            if (application != null){
                application.getDocuments().add(this);
                BridgeManager.getInstance().changeApplication(this, application);
                application.getBridge().add(this);
                application.onDocumentAdded(this);
            }
        }
    }

    @Override
    public boolean isVisible() {
        if (getVisible() == null){
            return hasParent();
        }
        return getVisible();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        if (!Utils.isEquals(this.title, title)){
            this.title = title;
            getBridge().setTitle(title);
        }
    }

    public void setModal(boolean modal) {
        if (this.modal != modal){
            this.modal = modal;
            getBridge().setModal(modal);
        }
    }

    public boolean isModal() {
        return modal;
    }
    
    @Override
    public final Dimension getParentSize() {
        if (!hasParent()){
            Application app = getApplication();
            if (app != null){
                return app.getScreenSize();
            }
            return null;
        }
        return getParent().getSize();
    }

    public void handleError(Throwable e) {
        if (application != null){
            application.handleError(e);
            return;
        }
        if (hasParent()){
            Document doc = getParent().getDocument();
            if (doc != null){
                doc.handleError(e);
                return;
            }
        }
        throw new RuntimeException(e);
    }

    public Image loadImage(Path path) {
        return BridgeManager.getInstance().loadImage(this, path);
    }
    
    public void setBasePath(Path path) {
        basePath = path;
    }

    public Path getBasePath(){
        if (basePath == null){
            
            Container parent = getParent();
            if (parent != null){
                
                Document doc = parent.getDocument();
                if (doc != null){
                    return doc.getBasePath();
                }
            }
            
            Application app = getApplication();
            if (app != null){
                return app.getBasePath();
            }
        }
        return basePath;
    }

    public PageSettings getPageSettings() {
        return pageSettings;
    }

    public void setPageSettings(PageSettings pageSettings) {
        if (this.pageSettings != pageSettings){
            this.pageSettings = pageSettings;
            getBridge().setPageSettings(pageSettings);
        }
    }

    public Theme getTheme() {
        return theme;
    }

    public void setTheme(Theme theme) {
        if (this.theme != theme){
            Theme oldTheme = this.theme;
            
            if (oldTheme != null){
                this.theme = null;
                oldTheme.removeListener(getThemeListener());
            }
            
            if (theme != null){
                this.theme = theme;
                theme.addListener(getThemeListener());
            }
        }
    }

    public final ITheme getDefaultTheme(){
        return BridgeManager.getInstance().getDefaultTheme(getImplementationType());
    }

    public Formatter<?> getFormatter(ValueType valueType){
        // TODO getFormatter(ValueType valueType)
        //      pegar do Theme.getFormatter()
        return new ObjectFormatter();
    }

    public void scrollToPage(int pageNo){
        getBridge().scrollToPage(pageNo);
    }

    @Override
    Container.ChildList<Control> createChildList() {
        return new DocChildList<Control>();
    }

    @Override
    protected IDocument getBridge() {
        return (IDocument) lookupBridge();
    }
    
    @Override
    final ThemeListener getThemeListener(){
        return (ThemeListener) getChildren();
    }

    @Override
    public Document clone() {
        Document c = (Document) super.clone();
        c.application = null;
        return c;
    }
    
    @Override
    public Document cloneTree() {
        return (Document) super.cloneTree();
    }

    private Object readResolve() throws ObjectStreamException {
        // FIXME to test this code
        Theme theme = this.theme;
        this.theme = null;
        setTheme(theme);
        return this;
    }

    
    /**
     * @author wellsantos@wellsantos.com
     * @since 0.01
     */
    class DocChildList<T extends Control> extends Container.ChildList<T> implements ThemeListener {
        
        private static final long serialVersionUID = 1L;

        final IParentThemeHandler getThemeHandler() {
            return (IParentThemeHandler) lookupBridge();
        }
        
        @Override
        public void onThemeAttach(Theme theme) {
            getThemeHandler().onThemeAttach(theme);
            if (getThemeHandler().isFireStyleChangesToChildren()){
                StyleWrapper styleWrapper = theme.lookupWrapper(Control.class.getName(), 0);
                fireChangesToChildren(styleWrapper, StyleAttribute.ALL, styleWrapper.getStyle());
            }
        }

        @Override
        public void onThemeDetach(Theme theme) {
            setTheme(null);
            getThemeHandler().onThemeDetach(theme);
            if (getThemeHandler().isFireStyleChangesToChildren()){
                StyleWrapper styleWrapper = theme.lookupWrapper(Control.class.getName(), 0);
                fireChangesToChildren(styleWrapper, StyleAttribute.ALL, styleWrapper.getStyle());
            }
        }

        @Override
        public <E> void onStyleAttributeChanged(StyleWrapper styleWrapper, StyleAttribute<E> attribute, E attributeValue) {
            getThemeHandler().onStyleAttributeChanged(styleWrapper, attribute, attributeValue);
            if (getThemeHandler().isFireStyleChangesToChildren()){
                fireChangesToChildren(styleWrapper, attribute, attributeValue);
            }
        }
        
        protected <E> void fireChangesToChildren(StyleWrapper styleWrapper, StyleAttribute<E> attribute, E attributeValue){
            for (Control control : this){
                control.getThemeListener().onStyleAttributeChanged(styleWrapper, attribute, attributeValue);
            }
        }
        
    }
    
}
